/*
* Copyright (C) 2019 ~ 2021 Uniontech Software Technology Co.,Ltd.
*
* Author:     yanghongwei <yanghongwei@uniontech.com>
*
* Maintainer: yanghongwei <yanghongwei@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define LIBUSB      0
#define UDEV        0
#define INOTIFY     0
#define FANOTIFY    1

#if LIBUSB  // 测试USB热插拔 libusb
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>

using namespace std;

int usb_hotplug_callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data);
void print_endpoint(struct libusb_device_descriptor *endpoint);

int main()
{
    cout << "Hello World!" << endl;
    int rc = 0;
    libusb_context *context;

    // 初始化libusb上下文
    rc = libusb_init(&context);
    if (rc != LIBUSB_SUCCESS) {
        cout << "libusb_init failed, error:" << rc << endl;
        return 0;
    }
    cout << "libusb_init success." << endl;

    // 注册插入设备回掉
    rc = libusb_hotplug_register_callback(context, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
                                          LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
                                          LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
                                          usb_hotplug_callback , NULL, NULL);
    // 注册移除设备回掉
    // It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. It is safe to call libusb_get_device_descriptor on a device that has left
    rc = libusb_hotplug_register_callback(context, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
                                          LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
                                          LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
                                          usb_hotplug_callback , NULL, NULL);

    while (true)
    {
        /* code */
        timeval tv = {1, 0};
        rc = libusb_handle_events_timeout(nullptr, &tv);
        if (rc < 0)
            fprintf(stderr, "fail to libusb_handle_events: %d, %s\n", rc, libusb_error_name(rc));
    }

    // 清理libusb上下文
    libusb_exit(context);

    return 0;
}

int usb_hotplug_callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
{
    cout << "usb_hotplug_callback:" << event << endl;
    // 设备描述符
    struct libusb_device_descriptor desc;
    int ret = libusb_get_device_descriptor(device, &desc);
    if (ret < 0) {
        std::cout << "failed to get device descriptor: " << libusb_error_name(ret);
        return 0;
    }

    // 配置描述符
    for (int i = 0; i < desc.bNumConfigurations; i++) {
        struct libusb_config_descriptor *config = nullptr;
        int ret = libusb_get_config_descriptor(device, i, &config);
        if (LIBUSB_SUCCESS != ret) {
            continue;
        }

        // 接口描述符
        for (int j = 0; j < config->bNumInterfaces; j++) {
            const struct libusb_interface *interface = &config->interface[j];
            if (interface->num_altsetting > 0) {
                const struct libusb_interface_descriptor *interface_desc = &interface->altsetting[0];
                interface_desc->bInterfaceClass;
                printf(" bDeviceClass: %02xh\n", interface_desc->bInterfaceClass);
                break;
            }
        }

        libusb_free_config_descriptor(config);

//        if (udev.interfaceClass > 0) {
//            break;
//        }
    }
//    print_endpoint(&desc);
    return 0;
}

void print_endpoint(struct libusb_device_descriptor *endpoint)
{
    // printf(" bEndpointAddress: %02xh\n", endpoint.e);
    // 00 在接口中定义 08大容量存储设备  ff厂商定义
    printf(" bDeviceClass: %02xh\n", endpoint->bDeviceClass);
    // printf(" wMaxPacketSize: %d\n", endpoint->wMaxPacketSize);
    // printf(" bInterval: %d\n", endpoint->bInterval);
    // printf(" bRefresh: %d\n", endpoint->bRefresh);
    // printf(" bSynchAddress: %d\n", endpoint->bSynchAddress);
}
#elif UDEV  // 测试USB热插拔 udev
/* udev_example1.c
 *
 * Copyright (C) 2014 Robert Milasan <rmilasan@suse.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * This example will get basic information about a specified network
 * device using libudev API.
 *
 */

#include <stdio.h>
#include <libudev.h>
#include <iostream>
#include <string.h>

#define SYSPATH "/sys/class/net"
#define USB_ADD_ACT 	"add"
#define USB_BIND_ACT 	"bind"
#define USB_REMOVE_ACT 	"remove"

#define USB_MEDIA_TYPE   0x06  // 数码相机、手机相册等
#define USB_STORAGE_TYPE 0x08  // U盘、移动硬盘等

#define BUS_UNBIND_INTERFACE 	"/sys/bus/usb/drivers/usb/unbind"
#define DEVICE_INTERFACE_CLASS 	"bInterfaceClass"

int main(int argc, char *argv[])
{
    std::cout << "Start monitoring usb devices.";

    static std::string last_device_interclass;
    static bool hot_plug_flag = false;

    /* Create the udev object */
    struct udev *udev = udev_new();
    if (!udev) {
        std::cout << "Can't create udev.";
        return -1;
    }

    struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev");
    udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", nullptr);
    udev_monitor_enable_receiving(mon);

    int fd = udev_monitor_get_fd(mon);

    while (true) {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        // Wait for monitor to return
        int ret = select(fd + 1, &fds, nullptr, nullptr, nullptr);
        if (ret <= 0) {
            break;
        }

        if (FD_ISSET(fd, &fds)) {
            struct udev_device *dev = udev_monitor_receive_device(mon);
            if (dev) {
                const char *bInterfaceClass = udev_device_get_sysattr_value(dev, DEVICE_INTERFACE_CLASS);
                if (bInterfaceClass) {
                    std::cout << "@@@" << bInterfaceClass << std::endl;
                    last_device_interclass = bInterfaceClass;
                }

                const char *act = udev_device_get_action(dev);
                if (act) {
                    if (0 == strcmp(act, USB_REMOVE_ACT)) { // 设备拔出
                        std::cout << "设备拔出" << std::endl;
                    } else if (0 == strcmp(act, USB_BIND_ACT)) { // 设备插入
                        std::cout << "设备插入" << std::endl;
                    }
                }

                udev_device_unref(dev);
            }
        }
    }

    udev_unref(udev);
    std::cout << "Stop monitoring usb devices.";
    return 0;
}
#elif INOTIFY   // 测试目录监控 inotify
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>

#define EVENT_NUM 12

char *event_str[EVENT_NUM] =
{
    "IN_ACCESS",
    "IN_MODIFY",
    "IN_ATTRIB",
    "IN_CLOSE_WRITE",
    "IN_CLOSE_NOWRITE",
    "IN_OPEN",
    "IN_MOVED_FROM",
    "IN_MOVED_TO",
    "IN_CREATE",
    "IN_DELETE",
    "IN_DELETE_SELF",
    "IN_MOVE_SELF"
};

int main(int argc, char *argv[])
{
    int fd;
    int wd;
    int len;
    int nread;
    char buf[BUFSIZ];
    struct inotify_event *event;
    int i;

    fd = inotify_init();
    if (fd < 0)
    {
        fprintf(stderr, "inotify_init failed\n");
        return -1;
    }

    wd = inotify_add_watch(fd, "/home/yanghongwei/test_watch", IN_ALL_EVENTS);
    if (wd < 0)
    {
        fprintf(stderr, "inotify_add_watch /home/yanghongwei/test_watch failed\n");
        return -1;
    }

    buf[sizeof(buf) - 1] = 0;
    while ((len = read(fd, buf, sizeof(buf) - 1)) > 0)
    {
        nread = 0;
        while (len > 0)
        {
            event = (struct inotify_event *)&buf[nread];
            for (i = 0; i<EVENT_NUM; i++)
            {
                if ((event->mask >> i) & 1)
                {
                    if (event->len > 0)
                        fprintf(stdout, "%s --- %s\n", event->name, event_str[i]);
                    else
                        fprintf(stdout, "%s --- %s\n", " ", event_str[i]);
                }
            }
            nread = nread + sizeof(struct inotify_event) + event->len;
            len = len - sizeof(struct inotify_event) - event->len;
        }
    }

    return 0;
}

#elif FANOTIFY  // fanotify 监控

#include <iostream>
#include "Fanotify.h"

int main(int argc, char **argv)
{
    for(int i=0; i<argc; i++) {
        std :: cout << argv[i] << std::endl ;
    }

    Fanotify notify ;
    notify.setNotifyObject(argv[1]) ;
    notify.startListen() ;

    return 0;
}

#endif
